package OpenToday;

import OpenToday.Configuration.ConfigurationForm;
import OpenToday.Configuration.OpenTodayConfiguration;
import OpenToday.GUI.AboutForm;
import OpenToday.GUI.IOpenTodayGUI;
import OpenToday.GUI.MainCanvas;
import OpenToday.GUI.MainForm;
import OpenToday.PIMHelper.PIMHelper;
import OpenToday.PIMHelper.PhoneCalendarEvent;
import OpenToday.PIMHelper.PhoneCalendarToDo;
import OpenToday.Tools.ExceptionUtils;
import OpenToday.Tools.TimeUtils;
import java.util.Date;
import java.util.Vector;
import javax.microedition.lcdui.Alert;
import javax.microedition.lcdui.AlertType;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.midlet.MIDlet;
import javax.microedition.pim.PIMException;

/**
 * Main class, MIDlet, registers commands, actions, controls main display and navigation
 *
 */
public class OpenTodayMain extends MIDlet implements CommandListener, IManager {
    //private Form mMainForm;

    private IOpenTodayGUI mMainGUI;
    private boolean m_alreadyStarted;
    private boolean m_paused;
    private RefresherThread m_refresherThread;
    private boolean m_mustRefresh;

    /* commands */
    private Command aboutCommand;
    private Command configurationCommand;
    private Command stopCommand;
    private Command exitCommand;

    /**
     * Main method, call the configutarion, determine the type of display, start thread
     * <br>
     * This method queries the configuataion to test if an text based screen or canvas screen should be used.
     * The class is also a CommandListener. Here Exit, About, Configuration and Stop are registered.
     * <br>
     * Finaly the @see RefresherThread is initialized.
     * @throws Exception IOException from canvas
     */
    public OpenTodayMain() throws Exception {
        ExceptionUtils.PHASE("OpenTodayMain");
        m_mustRefresh = true;

        ExceptionUtils.PHASE("OpenTodayMain - initializing OpenTodayConfiguration");
        OpenTodayConfiguration.getInstance().InitializeOpenTodayConfiguration(this);
        if (OpenTodayConfiguration.getInstance().get_TextBasedTodayScreen()) {
            mMainGUI = new MainForm(this);
        } else {
            mMainGUI = new MainCanvas(this);
        }

        Displayable formForCommands = mMainGUI.getDisplayable();
        formForCommands.addCommand(getExitCommand());
        formForCommands.addCommand(getAboutCommand());
        formForCommands.addCommand(getConfigurationCommand());
        formForCommands.addCommand(getStopCommand());
        formForCommands.setCommandListener(this);

        String details = System.getProperty("microedition.platform");


        m_alreadyStarted = false;
        m_paused = false;

        //starting refresher thread
        m_refresherThread = new RefresherThread(this);
    }

    /**
     * Command actions are: exit, configuration, about, stop.
     * @param command command to react on
     * @param displayable unused
     */
    public void commandAction(Command command, Displayable displayable) {
        if (command == exitCommand) {
            ExceptionUtils.PHASE("OnExitPressed");
            destroyApp(true);
        } else if (command == configurationCommand) {
            //load config form
            ConfigurationForm frm = new ConfigurationForm(this);
            switchDisplayable(frm);

        } else if (command == aboutCommand) {
            //synchronized (this) {
            ExceptionUtils.PHASE("OnAboutPressed");
            //load config form
            AboutForm frm = new AboutForm(this, this);
            switchDisplayable(frm);
        //}
        } else if (command == stopCommand) {
            ExceptionUtils.PHASE("OnMinimizePressed");
            Display.getDisplay(this).setCurrent(null);
        }
    }

    /**
     * start midlet (thread), synchronized, set mainGUI display
     */
    public void startApp() {
        synchronized (this) {
            ExceptionUtils.PHASE("startApp");
            if (!m_alreadyStarted) {
                m_alreadyStarted = true;
                Display.getDisplay(this).setCurrent(mMainGUI.getDisplayable());
                m_refresherThread.start();
            }
        }
    }

    /**
     * pause midlet, synchronized
     */
    public void pauseApp() {
        synchronized (this) {
            ExceptionUtils.PHASE("pauseApp");
            if (!m_paused) {
                m_paused = true;
                m_refresherThread.pause();
            }
        }
    }

    /**
     * resume midlet, synchronized
     */
    public void resumeApp() {
        synchronized (this) {
            ExceptionUtils.PHASE("resumeApp");
            if (m_paused) {
                m_mustRefresh = true;//after resuming we must do repaint
                m_paused = false;
                m_refresherThread.resume();
            }
        }
    }

    /**
     * set display by taking the Form to display
     * @param displayable
     */
    public void switchDisplayable(Displayable displayable) {
        ExceptionUtils.PHASE("switchDisplayable");
        Display.getDisplay(this).setCurrent(displayable);
    }

    /**
     * destroy midlet, not synchronized
     * stop thread, set display to null and saves configuration
     * @param unconditional unused
     */
    public void destroyApp(boolean unconditional) {
        ExceptionUtils.PHASE("destroyApp");
        m_refresherThread.stop();
        Display.getDisplay(this).setCurrent(null);
        try {
            PIMHelper.getInstance().Destroy();
            OpenTodayConfiguration.getInstance().Shutdown();
        } catch (PIMException ex) {
            ex.printStackTrace();
        } finally {
            super.notifyDestroyed();
        }
    }

    /**
     * queries and set the main screen
     * <br>
     * calculates the different dates, queries the PIM and writes them to screen
     * further events are only queried if less than max events to show
     */
    public void RefreshTodayScreen() {

        //ExceptionUtils.PHASE("start RefreshTodayScreen");
        try {
            ExceptionUtils.PHASE("get PIM");
            PIMHelper pim = PIMHelper.getInstance();
            ExceptionUtils.PHASE("get all events");

            OpenTodayConfiguration cnf = OpenTodayConfiguration.getInstance();
            int m_maxEvents = cnf.get_MaxEventsToShow();
            int daysToShow = cnf.get_DaysAheadToLook();

            long current_time = TimeUtils.GetCurrentTime().getTime();

            long current_day_start = TimeUtils.getDayStart(current_time);
            long current_day_end = current_day_start + TimeUtils.MS_PER_DAY; // end of the day
            long tomorrow_day_start = current_day_start + TimeUtils.MS_PER_DAY;
            long tomorrow_day_end = current_day_end + TimeUtils.MS_PER_DAY;
            long next_week = current_day_start + (daysToShow * TimeUtils.MS_PER_DAY) - 1;//almost next week

            /*
            ExceptionUtils.PHASE("current_time       " + new Date(current_time));
            ExceptionUtils.PHASE("current_day_start  " + new Date(current_day_start));
            ExceptionUtils.PHASE("current_day_end    " + new Date(current_day_end));
            ExceptionUtils.PHASE("tomorrow_day_start " + new Date(tomorrow_day_start));
            ExceptionUtils.PHASE("tomorrow_day_end   " + new Date(tomorrow_day_end));
            ExceptionUtils.PHASE("next_week          " + new Date(next_week));
            */

            Vector today_events = pim.getPhoneCalendarEvents(current_time, current_day_end);

            int i_today_events = 0;// vector can be null, size() is then not available
            if (null != today_events) {
                i_today_events = today_events.size();
            }
            // only get further events if less than max events to show
            Vector tomorrow_events = null;
            if (m_maxEvents >= i_today_events) {
                tomorrow_events = pim.getPhoneCalendarEvents(tomorrow_day_start, tomorrow_day_end);
            }
            int i_tomorrow_events = 0;
            if (null != tomorrow_events) {
                i_tomorrow_events = tomorrow_events.size();
            }

            Vector week_events = null;
            if (m_maxEvents >= i_today_events + i_tomorrow_events) {
                week_events = pim.getPhoneCalendarEvents(tomorrow_day_end, next_week);
            }


            // ToDo --------------
            /*
            Vector todo_events = pim.getToDoEvents(current_day_start, next_week);
            int i_todo_events = 0;
            if (null != todo_events) {
                i_todo_events = todo_events.size();
            }
            */
            try {
                m_mustRefresh = false;
                mMainGUI.Clear();
                //rebuilding screen
                ExceptionUtils.PHASE("start rebuild screen");

                //populating
                //events

                WriteEvents(today_events, "Today", m_maxEvents);
                WriteEvents(tomorrow_events, "Tomorrow", m_maxEvents - i_today_events);
                WriteEvents(week_events, "Week", m_maxEvents - i_today_events - i_tomorrow_events);
                //WriteToDos(todo_events, "ToDo", i_todo_events);

            } finally {
                mMainGUI.RepaintRequired();//making sure the GUI is repainted
            }

        } catch (Exception e) {
            // failed to retrieve elements due to error!
            //adding to GUI
            e.printStackTrace();
            Alert error = new Alert("Error", "Error while refreshing today screen:\n\rPhase:" + ExceptionUtils.getPhases() + "\n\r" + e.toString(), null, AlertType.ERROR);
            error.setTimeout(Alert.FOREVER);
            Display.getDisplay(this).setCurrent(error);
        }
    }

    /**
     * Just write down events to screen
     * @param events vector having the events
     * @param day print to screen
     * @param maxEvents number of events to show
     */
    private void WriteEvents(Vector events, String day, int maxEvents) {

        if (null == events) {
            return;
        }

        ExceptionUtils.PHASE("start " + day + " events loop has: " + events.size());

        if (events.size() > 0 && maxEvents > 0) {
            mMainGUI.AddDayLine(day);
        }

        //for (int eventIndex = 0; eventIndex < events.size(); eventIndex++) {
        // show only m_maxEvents
        for (int eventIndex = 0; eventIndex < events.size() && eventIndex < maxEvents; eventIndex++) {

            PhoneCalendarEvent event = (PhoneCalendarEvent) events.elementAt(eventIndex);

            //ExceptionUtils.PHASE("WriteEvents event " + eventIndex);

            if (event.isAllDayEvent()) {
                ExceptionUtils.PHASE("all day event: " + event.getEventTitle());
                mMainGUI.AddAllDayEventLine(event.getEventTitle());
            } else {
                ExceptionUtils.PHASE("a day event: " + event.getEventTitle());
                String startString = TimeUtils.CreateSimpleTimeString(event.getEventStartTime());
                mMainGUI.AddEventLine(event.isCurrentEvent(), startString, event.getEventTitle());
            }
        }
    }

    /**
     * Just write down ToDo's to screen
     * @param vector having the PhoneCalendarToDo
     * @param day print to screen
     * @param maxEvents number of events to show
     */
/**    private void WriteToDos(Vector events, String day, int maxEvents) {

        if (null == events) {
            return;
        }

        ExceptionUtils.PHASE("start " + day + " todo loop has: " + events.size());

        if (events.size() > 0 && maxEvents > 0) {
            mMainGUI.AddDayLine(day);
        }

        //for (int eventIndex = 0; eventIndex < events.size(); eventIndex++) {
        // show only m_maxEvents
        for (int eventIndex = 0; eventIndex < events.size() && eventIndex < maxEvents; eventIndex++) {

            PhoneCalendarToDo event = (PhoneCalendarToDo) events.elementAt(eventIndex);

            ExceptionUtils.PHASE("todo " + eventIndex);

            if (event.isAllDayEvent()) {
                ExceptionUtils.PHASE("all day event: " + event.getEventTitle());
                mMainGUI.AddAllDayEventLine(event.getEventTitle());
            } else {
                ExceptionUtils.PHASE("a day event: " + event.getEventTitle());
                String startString = TimeUtils.CreateSimpleTimeString(event.getEventStartTime());
                mMainGUI.AddEventLine(event.isCurrentEvent(), startString, event.getEventTitle());
            }
 
        }
    }
**/

    /**
     * creates an alert if an error occures
     * @param errorString Message to show
     */
    public void OnError(String errorString) {
        //synchronized (this) {
        Alert error = new Alert("Error", "Error encounterred:\n\r" + errorString, null, AlertType.ERROR);
        error.setTimeout(Alert.FOREVER);
        Display.getDisplay(this).setCurrent(error);
    //}

    }

    /**
     * creates an alert, shows message
     * @param message Message to show
     */
    public void OnMessage(String message) {
        //synchronized (this) {
        Alert msg = new Alert("Message", message, null, AlertType.INFO);
        msg.setTimeout(Alert.FOREVER);
        Display.getDisplay(this).setCurrent(msg);
    //}
    }

    /** Returns an initiliazed instance of aboutCommand component.
     * @return the initialized component instance
     */
    public Command getAboutCommand() {
        if (aboutCommand == null) {
            aboutCommand = new Command("About", Command.SCREEN, 0);
        }
        return aboutCommand;
    }

    /**
     * Returns an initiliazed instance of configurationCommand component.
     * @return the initialized component instance
     */
    public Command getConfigurationCommand() {
        if (configurationCommand == null) {
            configurationCommand = new Command("Configuration", Command.SCREEN, 0);
        }
        return configurationCommand;
    }

    /**
     * Returns an initiliazed instance of stopCommand (Minimize) component.
     * @return the initialized component instance
     */
    public Command getStopCommand() {
        if (stopCommand == null) {
            stopCommand = new Command("Minimize", Command.STOP, 0);
        }
        return stopCommand;
    }

    /**
     * Returns an initiliazed instance of exitCommand component.
     * @return the initialized component instance
     */
    public Command getExitCommand() {
        if (exitCommand == null) {
            exitCommand = new Command("Exit", Command.EXIT, 0);
        }
        return exitCommand;
    }

    /**
     * use @see #switchDisplayable(Displayable displayable) to display main gui
     */
    public void displayYourself() {
        switchDisplayable(mMainGUI.getDisplayable());
    }
}